#include <new>
#include <stdio.h>
#include <error.h>
#include <sys/mman.h>
#include "MemPool.h"
#include "MemBlocks.h"
#include "MemChunks.h"

/**
 * 内存块数组元素结构体
 * nSize : 块长度
 * pBlocks : 指向内存块
 */
struct BlockIndx
{
    unsigned int nSize;
    CMemBlocks *pBlocks;
};

CMemPool *CMemPool::m_pInstance = nullptr;

CMemPool *CMemPool::GetInstance()
{
    if (nullptr != m_pInstance)
        return m_pInstance;

    CMemPool *mp = nullptr;
    CMemChunks *mc = nullptr;
    void *ptr = AllocHeader();

    if (nullptr == ptr)
        return nullptr;

    mp = new (ptr) CMemPool();

    if (nullptr == mp)
    {
        FreeHeader(ptr);
        return nullptr;
    }

    mc = new ((unsigned char *)ptr + sizeof(CMemPool)) CMemChunks();

    if (nullptr == mc)
    {
        FreeHeader(ptr);
        return nullptr;
    }

    mp->m_pChunks = mc;
    m_pInstance = mp;

    return mp;
}

void *CMemPool::AllocHeader()
{
    int iHeadSize = sizeof(CMemPool) + sizeof(CMemChunks);
    void *ptr = mmap(0, iHeadSize, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, 0, 0);

    if (MAP_FAILED == ptr)
    {
        printf("Error: mmap %d\tfile=%s\tline=%d\n", errno, __FILE__, __LINE__);
        return nullptr;
    }

    return ptr;
}

bool CMemPool::FreeHeader(void *ptr)
{
    int iRet = -1;
    int iHeadSize = sizeof(CMemPool) + sizeof(CMemChunks);

    if (nullptr == ptr)
    {
        printf("Error: (nullptr == ptr)\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return false;
    }

    iRet = munmap(ptr, iHeadSize);

    if (0 != iRet)
        printf("Error: munmap %d\tfile=%s\tline=%d\n", errno, __FILE__, __LINE__);

    return (0 == iRet);
}

CMemPool::CMemPool()
{
    m_nMaxSize = 0;
    m_nMaxLevel = 0;
    m_pBlockIndx = nullptr;
    m_pChunks = nullptr;
}

CMemPool::~CMemPool()
{
    Destroy();
}

bool CMemPool::Initialize(const unsigned int nMaxSize, const unsigned int nMaxLevel)
{
    void *ptr = nullptr;
    unsigned int nSize;
    unsigned int nAlign = sizeof(void *);
    float fAvg = nMaxSize / nMaxLevel;

    if (nullptr == m_pChunks)
        return false;

    m_nMaxSize = nMaxSize;
    m_nMaxLevel = nMaxLevel;
    ptr = m_pChunks->Alloc(sizeof(BlockIndx) * (nMaxLevel + 1));

    if (nullptr == ptr)
        return false;

    m_pBlockIndx = new (ptr) BlockIndx[nMaxLevel + 1];

    if (nullptr == m_pBlockIndx)
    {
        printf("Error (nullptr == m_pBlockIndx):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return false;
    }

    for (size_t iLoop = 0; iLoop <= nMaxLevel; iLoop++)
    {
        ptr = m_pChunks->Alloc(sizeof(CMemBlocks));

        if (nullptr == ptr)
        {
            Destroy();
            printf("Error (nullptr == ptr):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
            return false;
        }

        if (iLoop == nMaxLevel)
        {
            m_pBlockIndx[iLoop].nSize = 0;
            m_pBlockIndx[iLoop].pBlocks = new (ptr) CMemBlocks(LARGE_BLOCK, iLoop, 0, m_pChunks);
        }
        else
        {
            // 获得字节数对齐的大小
            // nSize = ((unsigned int)(fAvg * (iLoop + 1) / nAlign) + 1) * nAlign;
            nSize = ((unsigned int)((fAvg + nAlign - 1) * (iLoop + 1) / nAlign)) * nAlign;
            m_pBlockIndx[iLoop].nSize = nSize;
            m_pBlockIndx[iLoop].pBlocks = new (ptr) CMemBlocks(SMALL_BLOCK, iLoop, nSize, m_pChunks);
        }

        if (nullptr == m_pBlockIndx[iLoop].pBlocks)
        {
            Destroy();
            printf("Error (nullptr == m_pBlockIndx[iLoop].pBlocks):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
            return false;
        }
        else
        {
            bool bRet = m_pBlockIndx[iLoop].pBlocks->Initialize();

            if (!bRet)
            {
                Destroy();
                printf("Error (!bRet):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
                return false;
            }
        }
    }

    return true;
}

void CMemPool::Destroy()
{
    if (nullptr != m_pChunks)
    {
        // 所有的内存都是从MemChunks里分配的,所以只需释放全部大块即可
        m_pChunks->Destroy();
        delete m_pChunks;
        m_pChunks = nullptr;
    }

    // 释放本对象所占用的空间
    FreeHeader(this);
}

void *CMemPool::Alloc(unsigned int nLength)
{
    void *pBlock = nullptr;
    CMemBlocks *pMemBlocks = GetRightBlocks(nLength);

    if (nullptr != pMemBlocks)
    {
        pBlock = pMemBlocks->Alloc(nLength);
    }

    return pBlock;
}

bool CMemPool::Free(void *pExtBlock)
{
    bool bRet = false;
    BlockNode *pIntBlock = nullptr;

    if (nullptr == pExtBlock)
    {
        printf("Error (nullptr == pExtBlock):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return false;
    }

    pIntBlock = (BlockNode *)((char *)pExtBlock - sizeof(BlockNode));

    if (nullptr == pIntBlock->Belong())
    {
        printf("Error (nullptr == pIntBlock->pList):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return false;
    }

    // 获得数据所属链表，链表所在哪个内存块
    CMemBlocks *pMemBlocks = (CMemBlocks *)pIntBlock->Belong()->GetData();

    if (nullptr != pMemBlocks)
    {
        unsigned int nIndx = pMemBlocks->GetIndex();

        if (nIndx <= m_nMaxLevel)
        {
            BlockType type = pMemBlocks->GetBlockType();
            unsigned int nSize = pMemBlocks->GetBlockSize();

            if (SMALL_BLOCK == type)
            {
                // 判断内存块大小是否一致
                bRet = (m_pBlockIndx[nIndx].nSize == nSize);
            }
            else if (LARGE_BLOCK == type)
            {
                // 判断内存块索引是否一致
                bRet = ((nIndx == m_nMaxLevel) && (0 == m_pBlockIndx[nIndx].nSize));
            }

            if (bRet)
            {
                bRet = pMemBlocks->Free(pIntBlock);
            }
        }
    }

    return bRet;
}

// TODO: 算法需与has_map比较
CMemBlocks *CMemPool::GetRightBlocks(unsigned int nLength)
{
    CMemBlocks *pMemBlocks = nullptr;

    if (nullptr == m_pBlockIndx)
    {
        printf("Error (nullptr == m_pBlockIndx):\tfile=%s\tline=%d\n", __FILE__, __LINE__);
        return nullptr;
    }

    if (nLength <= m_pBlockIndx[0].nSize)
    {
        pMemBlocks = m_pBlockIndx[0].pBlocks;
    }
    else if (nLength <= m_pBlockIndx[1].nSize)
    {
        pMemBlocks = m_pBlockIndx[1].pBlocks;
    }
    else if (nLength <= m_pBlockIndx[2].nSize)
    {
        pMemBlocks = m_pBlockIndx[2].pBlocks;
    }
    else if (nLength <= m_pBlockIndx[3].nSize)
    {
        pMemBlocks = m_pBlockIndx[3].pBlocks;
    }
    else if (nLength > m_pBlockIndx[m_nMaxLevel - 1].nSize)
    {
        pMemBlocks = m_pBlockIndx[m_nMaxLevel - 1].pBlocks;
    }
    else
    {
        int iLeft = 3;
        int iRight = m_nMaxLevel - 1;
        int iMiddle;
        int iIndx = 0;
        unsigned int nSize;

        while (iRight - iLeft > 1)
        {
            iMiddle = (iLeft + iRight) / 2;
            nSize = m_pBlockIndx[iMiddle].nSize;

            if (nLength < nSize)
            {
                iRight = iMiddle;
            }
            else if (nLength > nSize)
            {
                iLeft = iMiddle;
            }
            else
            {
                iIndx = iMiddle;
                break;
            }
        }

        if (0 == iIndx)
        {
            iIndx = iRight;
        }

        pMemBlocks = m_pBlockIndx[iIndx].pBlocks;
    }

    return pMemBlocks;
}
